#include <vector>
#include <cstdint>
#include <memory>
#include <string>

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <netdb.h>

#include <tenacitas_command/receiver.h>

namespace ten_cm = tenacitas::command;

namespace tenacitas {
    namespace command {
        namespace test2 {

            struct message_types {
                typedef std::string message;
                typedef std::shared_ptr<message> message_ptr;
            };

            struct app_data_types {
                typedef message_types::message app_data;
                typedef message_types::message_ptr app_data_ptr;
            };
            

            struct connection_types {
                    
                struct connection {
                    typedef std::shared_ptr<connection> ptr;

                    connection( int p_socket )
                        : m_socket( p_socket ) {}

                    connection( ):
                        m_socket( -1 ) {}

                    ~connection( ) {
                        if ( m_socket != -1 ) {
                            ::close( m_socket );
                        }
                    }
                    

                    bool operator==( int p_i ) {
                        return ( p_i == m_socket );
                    }

                    connection & operator =( int p_i ) {
                        m_socket = p_i;
                        return *this;
                    }

                    operator int( ) {
                        return m_socket;
                    }
                private:
                    int m_socket;
                };

                typedef connection::ptr connection_ptr;
            };

            struct connector_types {

                /** a socket is a surrogate to an internet connection (IP +
                    port ) */		
		
                struct connector {
                    typedef connection_types::connection connection;
                    typedef connection_types::connection_ptr connection_ptr;


                    connector( const std::string & p_service, int p_qlen )
                        : m_listener( ),
                          m_service( p_service ),
                          m_qlen( p_qlen ) { }
                    
                    bool start( ) {
                        if ( !m_listener ) {
                            struct servent *pse = nullptr;
                            struct protoent *ppe = nullptr;
                            struct sockaddr_in sin;
                            int type;

                            memset( &sin, 0, sizeof(sin) );

                            sin.sin_family = AF_INET;
                            sin.sin_addr.s_addr = INADDR_ANY;
                            pse = getservbyname( m_service.c_str( ), "tcp" );

                            if ( pse ) { 
                                sin.sin_port = htons(
                                    ntohs( ( unsigned short )pse->s_port ) ); 
                            }

                            else if ( ( sin.sin_port =
                                        htons( ( unsigned short )
                                               atoi( m_service.c_str( ) ) ) ) == 0 ) {  
                                return false;
                            }

                            ppe = getprotobyname( "tcp" );
                            
                            if ( ppe == 0 ) {
                                return false;
                            }

                            type = SOCK_STREAM;

                            int s = socket( PF_INET, type, ppe->p_proto );

                            if ( s < 0 ) {
                                return false;
                            }

                            if ( ::bind( s, ( struct sockaddr * )&sin,
                                         sizeof( sin ) ) < 0 ) { 
                                return false;
                            }

                            if ( listen( s, m_qlen ) < 0 ) {
                                return false;
                            }

                            m_listener = connection_ptr( new connection( s ) );
                        }
                        return true;
                    }

                    connection_ptr connected( ) {
                        unsigned int alen;
                        struct sockaddr_in fsin;
                        alen = sizeof( fsin );
                        int s = accept( *m_listener,
                                        ( struct sockaddr * )&fsin,
                                        &alen ); 
                        if ( s < 0 ) {
                            return connection_ptr( );
                        }
                        return connection_ptr( new connection( s ) );
                    }

                private:
                    connection_ptr m_listener;
                    std::string m_service;
                    int m_qlen;
                };


                typedef std::shared_ptr<connector> connector_ptr;

                struct connector_factory {
                    connector_factory( const std::string & p_service,
                                       int p_qlen = 32 )
                        : m_service( p_service ),
                          m_qlen ( p_qlen ) { }

                    connector_ptr create( ) {
                        return connector_ptr( new connector( m_service, m_qlen ) );
                    }

                private:
                    std::string m_service;
                    int m_qlen;
                };

                typedef std::shared_ptr<connector_factory> connector_factory_ptr;
            };


            struct message_io_types {

                struct message_io {

                    typedef connection_types::connection_ptr connection_ptr;
                    typedef message_types::message message;                    
                    typedef message_types::message_ptr message_ptr;
                    
                    message_ptr read( connection_ptr & p_con ) {

                        char l_buf[2048];
                        int16_t l_counter;

                        while ( ( l_counter = ::read( *p_con, l_buf, 2048 ) ) ) {
                            if ( l_counter < 0 ) {
                                return message_ptr( );
                            }
                        }
                        
                        return message_ptr( new message( l_buf ) );
                    }

                    bool write( connection_ptr p_con,
                                const message_ptr p_message ) {
                        
                        if ( ::write( *p_con,
                                      p_message->c_str( ),
                                      p_message->size( ) ) < 0 ) {
                            return false;
                        }
                        return true;
                    }
                };

                typedef std::shared_ptr<message_io> message_io_ptr;

                struct message_io_factory {
                    message_io_ptr create( ) {
                        return message_io_ptr( new message_io( ) );
                    }
                };

                typedef std::shared_ptr<message_io_factory> message_io_factory_ptr;
            };


            struct translator_types {

                typedef message_types::message message;
                typedef message_types::message_ptr message_ptr;

                typedef app_data_types::app_data app_data;
                typedef app_data_types::app_data_ptr app_data_ptr;
                
                
                struct translator {
                    app_data_ptr to_app_data( const message_ptr p_message ) {
                        return p_message;
                    }

                    message_ptr to_message( const app_data_ptr p_app_data ) {
                        return p_app_data;
                    }
                };

                typedef std::shared_ptr<translator> translator_ptr;

                struct translator_factory {
                    translator_ptr create( ) {
                        return translator_ptr( new translator( ) );
                    }
                };

                typedef std::shared_ptr<translator_factory> translator_factory_ptr;
            };
            
            
            struct application_types {

                typedef connection_types::connection_ptr connection_ptr;
                typedef message_io_types::message_io_ptr message_io_ptr;
                typedef app_data_types::app_data_ptr app_data_ptr;
                typedef translator_types::translator_factory_ptr
                translator_factory_ptr; 
                typedef translator_types::translator translator;
                typedef translator_types::translator_ptr translator_ptr;

                struct application {
	  
                    application( translator_ptr p_translator )
                        : m_translator( p_translator ) {}
                    
                    void operator( )( const app_data_ptr p_app_data,
                                      connection_ptr p_con,
                                      message_io_ptr p_message_io ) {
                        message_types::message_ptr l_message =
                            m_translator->to_message( p_app_data );
                        
                        p_message_io->write( p_con, l_message );
                    }

                private:
                    translator_ptr m_translator;
                };

                typedef std::shared_ptr<application> application_ptr;

                struct application_factory {

                    application_factory( translator_factory_ptr p_translator_factory )
                        : m_translator_factory( p_translator_factory ) {}

                    application_ptr create( ) {
                        application_ptr l_rc(
                            new application( m_translator_factory->create( ) ) ); 
                        return l_rc;
                    }                    
	  
                private:
                    translator_factory_ptr m_translator_factory;
                };

                typedef std::shared_ptr<application_factory> application_factory_ptr;
            };

            struct timeout_types {
                typedef int16_t timeout;
                typedef timeout * timeout_ptr;
                struct timeout_factory {
                    timeout_ptr create( ) {
                        return timeout_ptr();
                    }
                };
                typedef timeout_factory * timeout_factory_ptr;
            };
	    
            struct concurrence_types {

                /** no concurrence */
                struct concurrence {

                    typedef command::processor<message_types,
                                               connection_types,
                                               app_data_types,
                                               translator_types,
                                               application_types,
                                               timeout_types,
                                               message_io_types> processor;

                    void submit( processor & p_processor ) { 
                        p_processor( );
                    }
                };

                typedef std::shared_ptr<concurrence> concurrence_ptr;

                struct concurrence_factory {
                    concurrence_ptr create( ) {
                        return concurrence_ptr( new concurrence( ) );
                    }
                };

                typedef std::shared_ptr<concurrence_factory> concurrence_factory_ptr;
            };
            

            typedef receiver <
                message_types,
                connection_types,
                app_data_types,
                translator_types,
                concurrence_types,
                application_types,
                connector_types,
                timeout_types,
                message_io_types> test2_receiver;

            typedef translator_types::translator_factory translator_factory;
            typedef translator_types::translator_factory_ptr translator_factory_ptr;

            typedef connector_types::connector_factory connector_factory;
            typedef connector_types::connector_factory_ptr connector_factory_ptr;

            typedef concurrence_types::concurrence_factory concurrence_factory;
            typedef concurrence_types::concurrence_factory_ptr concurrence_factory_ptr;

            typedef application_types::application_factory application_factory; 
            typedef application_types::application_factory_ptr application_factory_ptr; 

            typedef connector_types::connector_factory connector_factory;
            typedef connector_types::connector_factory_ptr connector_factory_ptr;

            typedef timeout_types::timeout_factory timeout_factory;
            typedef timeout_types::timeout_factory_ptr timeout_factory_ptr;

            typedef message_io_types::message_io_factory message_io_factory;
            typedef message_io_types::message_io_factory_ptr message_io_factory_ptr;

            static bool run( ) {

                translator_factory_ptr
                    l_translator_factory( new translator_factory( ) ); 

                concurrence_factory_ptr
                    l_concurrence_factory( new concurrence_factory( ) );

                application_factory_ptr
                    l_application_factory(
                        new application_factory( l_translator_factory ) );  

                connector_factory_ptr
                    l_connector_factory( new connector_factory( "45001" ) );

                timeout_factory_ptr
                    l_timeout_factory( new timeout_factory( ) );

                message_io_factory_ptr
                    l_message_io_factory( new message_io_factory( ) );

                test2_receiver l_receiver( l_translator_factory,
                                           l_concurrence_factory,
                                           l_application_factory,
                                           l_connector_factory,
                                           l_timeout_factory,
                                           l_message_io_factory );
				   

                l_receiver.start_sync( );
                
                return true;
            }

        }
    }
    
}
        
/* }}} */
